#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <thread>
#include <QtSerialPort/QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
#include <QLabel>
#include <QToolButton>
#include <QComboBox>
#include <QThread>
#include <QQueue>

#include "qcustomplot.h"
#include "lwrb.h"

#include "ctrackgenerate.h"

#include "MotorControllerUserConfig.h"


QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE


// CAN ID 11 bit high to low
#define ID_ECHO_BIT         0x400 // 1 bit
#define ID_NODE_BIT         0x3E0 // 5 bit
#define ID_CMD_BIT          0x01F // 5 bit

#define IS_ECHO(can_id)     (can_id & ID_ECHO_BIT)
#define GET_NODE_ID(can_id) ((can_id & ID_NODE_BIT) >> 5)
#define GET_CMD(can_id)     (can_id & ID_CMD_BIT)



enum eSerialCmd
{
    CAN_CMD_MOTOR_DISABLE = 0,
    CAN_CMD_MOTOR_ENABLE,

    CAN_CMD_SET_TORQUE,
    CAN_CMD_SET_VELOCITY,
    CAN_CMD_SET_POSITION,

    CAN_CMD_CALIB_START,
    CAN_CMD_CALIB_REPORT,
    CAN_CMD_CALIB_ABORT,

    CAN_CMD_ANTICOGGING_START,
    CAN_CMD_ANTICOGGING_REPORT,
    CAN_CMD_ANTICOGGING_ABORT,

    CAN_CMD_SET_HOME,
    CAN_CMD_ERROR_RESET,
    CAN_CMD_GET_STATUSWORD,
    CAN_CMD_STATUSWORD_REPORT,

    CAN_CMD_GET_VALUE_1,
    CAN_CMD_GET_VALUE_2,

    CAN_CMD_SET_CONFIG,
    CAN_CMD_GET_CONFIG,
    CAN_CMD_SAVE_ALL_CONFIG,
    CAN_CMD_RESET_ALL_CONFIG,

    CAN_CMD_SYNC,
    CAN_CMD_HEARTBEAT,

    CAN_CMD_GET_FW_VERSION = 28,

    CAN_CMD_DFU_START,
    CAN_CMD_DFU_DATA,
    CAN_CMD_DFU_END,
};

enum eDataType
{
    DATA_TYPE_TORQUE = 0,
    DATA_TYPE_ENCODER_VEL,
    DATA_TYPE_ENCODER_POS,
    DATA_TYPE_IQ_FILT,
    DATA_TYPE_VBUS_FILT,
    DATA_TYPE_IBUS_FILT,
    DATA_TYPE_POWER_FILT
};

enum eControlMode{
    CONTROL_MODE_TORQUE_RAMP      = 0,
    CONTROL_MODE_VELOCITY_RAMP    = 1,
    CONTROL_MODE_POSITION_FILTER  = 2,
    CONTROL_MODE_POSITION_PROFILE = 3,
};

enum eConfigDataType
{
    CONFIG_DATA_TYPE_FLOAT,
    CONFIG_DATA_TYPE_INT32
};

typedef struct SerialFrame
{
    uint32_t head : 8; // 帧头，固定为0xAA
    uint32_t id : 16;
    uint32_t dlc : 8;
    uint8_t  data[8];
    uint32_t crc;
} SerialFrame_t;


class CSerialDataConsumer;
class MainWindow;


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    // 初始化界面等操作
    void InitSerilaPort();
    void InitUI();
    void InitToolBar();
    void InitStateBar();
    void InitDebugUI();
    void InitCalibUI();
    void InitConfigUI();


    // 串口数据发送
    int SerialSend(QByteArray data);
    // 数据包制作
    QByteArray MakeSerialCmdPackage(eSerialCmd cmd, int len = 0, float val = 0, int cfgIdx = 0);

    // 根据参数名称找到输入控件并返回数值
    float GetConfigInputCtrlValByConfigName(QString param);


protected:
    bool nativeEvent(const QByteArray &eventType, void *message, long *result);


private slots:

    // 串口命令下发定时器的槽函数
    void slot_TimSerialCmdSend_Timeout();
    // 串口接收槽函数
    void slot_Serial_ReadyRead();
    // 绘图定时器槽函数
    void slot_TimQcpPlot_Timeout();

    // toolbar区域
    void slot_BtnConnect_Clicked();
    void slot_BtnDebug_Clicked();
    void slot_BtnCalib_Clicked();
    void slot_BtnConfig_Clicked();
    void slot_BtnClearError_Clicked();

    // “调试”界面上波形显示按钮区的槽函数
    void slot_BtnDebugPageScope1Work_Clicked();
    void slot_BtnDebugPageScope2Work_Clicked();

    void slot_BtnDebugPageStartMotor_Clicked();
    void slot_BtnDebugPageStopMotor_Clicked();
    void slot_BtnDebugPageResetHome_Clicked();

    void slot_BtnDebugPageSetTorque_Clicked();
    void slot_BtnDebugPageSetVel_Clicked();
    void slot_BtnDebugPageSetPos_Clicked();

    void slot_BtnTrackGeneratePageSendPos(float val);

    void slot_QcpDebugPageScope_MouseMove(QMouseEvent *e);
    void slot_QcpDebugPageScope_RightClicked(QMouseEvent * event);
    void slot_ActionDebugPageScopeResetGraph_Triggered();

    // 系统状态信息显示槽函数，串口数据解析线程通过发送信号来触发
    void slot_ShowSystemStateInfo(QString info);
    void slot_ShowTxErrInfo(QString info);
    void slot_ShowRxErrInfo(QString info);
    void slot_ShowSerialStateInfo(QString info);
    void slot_SetMotorEnableLed(bool en);
    void slot_SetTargetReachedLed(bool en);
    void slot_SetCurrentLimitActiveLed(bool en);

    // “校准”界面控件的槽函数
    void slot_BtnCalibPageStartCalibMotorAndEncoder_Clicked();
    void slot_BtnCalibPageStartCalibAnticogging_Clicked();
    void slot_SetCalibPageMotorR(float r);
    void slot_SetCalibPageMotorL(float l);
    void slot_SetCalibPageMotorPP(int pp);
    void slot_SetCalibPageEncoderDir(int dir);
    void slot_SetCalibPageEncoderOffset(int offset);

    // “配置”界面控件的槽函数
    void slot_BtnConfigPageSaveConfig_Clicked();
    void slot_BtnConfigPageResetAllConfig_Clicked();
    void slot_BtnConfigPageReadWriteConfig_Clicked(); // “配置”界面读写参数按钮的槽函数
    void slot_BtnConfigPageReadAllConfig_Clicked();
    //void slot_BtnConfigPageWriteAllConfig_Clicked();

    // 读取配置的槽函数，信号由串口数据解析线程发送
    void slot_ReadConfig(QString param, int dataType, float val);


    void slot_ShowWarningMessageBox(QString title, QString text)
    {
        QMessageBox::warning(this, title, text);
    }

private:
    Ui::MainWindow *ui;

    // 保存用户配置
    tUsrConfig* m_focUserCfg = new tUsrConfig;

    // 串口句柄
    QSerialPort* m_serialPort;
    QMutex* m_mutex_serialPort;
    // 串口ring buffer
    lwrb_t m_serialLwrb;
    static constexpr int m_serialLwrbBuffSize = sizeof(SerialFrame_t) * 10000;
    uint8_t m_serialLwrbBuff[m_serialLwrbBuffSize];

    // 串口命令下发定时器
    QTimer* m_tim_serialCmdSend;
    // 串口队列数据消费线程
    CSerialDataConsumer* m_serialDataConsumer;
    // 绘图定时器
    QTimer* m_tim_qcpPlot;
    // “调试”界面两个示波器的数据队列
    QQueue<float>* m_queue_debugPageScope1;
    QQueue<float>* m_queue_debugPageScope2;
    // “校准”界面两个示波器的校准数据容器
    QVector<float>* m_vector_calibPageScope1;
    QVector<float>* m_vector_calibPageScope2;

    ////////////////////////// 工具栏左侧按钮 //////////////////////////
    QToolButton* m_btn_debug;
    QToolButton* m_btn_calib;
    QToolButton* m_btn_config;
    QToolButton* m_btn_track;
    QToolButton* m_btn_about;
    ////////////////////////// 工具栏右侧区域 //////////////////////////
    QComboBox* m_cbox_serial;
    QComboBox* m_cbox_baudrate;
    QToolButton* m_btn_connect;
    QToolButton* m_btn_clearError;

    ////////////////////////// 状态栏 //////////////////////////
    QLabel* m_label_systemState;
    QLabel* m_label_serialState;
    QLabel* m_label_txErr;
    QLabel* m_label_rxErr;

    ////////////////////////// 调试界面 //////////////////////////

    bool m_is_useExternalData; // 设置扭矩、转速、位置时使用外部数据
    CTrackGenerate* m_trackGenerate; // 位控模式下的曲线轨迹规划
    QWidget* m_widget_debug;
    QLabel* m_label_debugPage_cursor1, *m_label_debugPage_cursor2;
    QComboBox* m_cbox_debugPage_source1, *m_cbox_debugPage_source2;
    QComboBox* m_cbox_debugPage_sampleCount1, *m_cbox_debugPage_sampleCount2;
    QToolButton* m_btn_debugPage_scope1Work, *m_btn_debugPage_scope2Work;
    QCustomPlot* m_qcp_debugPage_scope1, *m_qcp_debugPage_scope2;
    QCPItemTracer *m_qcpcursor_debugPageScope1, *m_qcpcursor_debugPageScope2;
    QLabel* m_label_motorEnable;
    QLabel* m_label_targetReached;
    QLabel* m_label_currentLimitActive;
    QLineEdit* m_lineEdit_debugPage_torque;
    QLineEdit* m_lineEdit_debugPage_vel;
    QLineEdit* m_lineEdit_debugPage_pos;
    QDoubleSpinBox* m_num_debugPage_torque;
    QDoubleSpinBox* m_num_debugPage_vel;
    QDoubleSpinBox* m_num_debugPage_pos;
    QPushButton* m_btn_debugPage_sendTorque;
    QPushButton* m_btn_debugPage_sendVel;
    QPushButton* m_btn_debugPage_sendPos;
    QPushButton* m_btn_debugPage_runMotor;
    QPushButton* m_btn_debugPage_stopMotor;
    QPushButton* m_btn_debugPage_resetHome;
    bool m_is_debugPageScope1_working = false, m_is_debugPageScope2_working = false;
    eDataType m_debugPage_currentDataType1, m_debugPage_currentDataType2;

    ////////////////////////// 校准界面 //////////////////////////
    QWidget* m_widget_calib;
    QCustomPlot* m_qcp_calibPage_encoderCalib;
    QLabel* m_label_calibPage_motorR;
    QLabel* m_label_calibPage_motorL;
    QLabel* m_label_calibPage_motorPP;
    QLabel* m_label_calibPage_encoderDir;
    QLabel* m_label_calibPage_encoderOffset;
    QPushButton* m_btn_calibPage_startMotorAndEncoderCalib;
    QCustomPlot* m_qcp_calibPage_anticogCalib;
    QPushButton* m_btn_calibPage_startAnticogCalib;

    ////////////////////////// 配置界面 //////////////////////////
    QWidget* m_widget_config;
    QPushButton* m_btn_cfgPage_readAll;
    QPushButton* m_btn_cfgPage_writeAll;
    QPushButton* m_btn_cfgPage_resetDefault;
    QPushButton* m_btn_cfgPage_saveConfig;

    ///
    /// 1. 配置界面的超多控件......分类保存。
    /// 2. 每个配置项都有上传、下载按钮，但是所有按钮只有一个槽函数，
    /// 在创建按钮对象时，设置按钮的object name，在槽函数中根据name执行对应动作
    /// name格式：read_invertDir    write_invertDir
    ///
    ///

    // 配置项及其对应的索引
    QMap<QString, int>* m_map_configToIndex;
    // 配置项索引及其对应的配置项
    QMap<int, QString>* m_map_indexToConfig;
    // 配置项对应的数据类型
    QMap<QString, int>* m_map_configToDataType;

    // 电机
    QComboBox* m_cbox_configPage_invertDir;
    QPushButton* m_btn_configPage_read_invertDir, *m_btn_configPage_write_invertDir;
    QLineEdit* m_lineEdit_configPage_inertia;
    QPushButton* m_btn_configPage_read_inertia, *m_btn_configPage_write_inertia;
    QLineEdit* m_lineEdit_configPage_torqueConstant;
    QPushButton* m_btn_configPage_read_torqueConstant, *m_btn_configPage_write_torqueConstant;
    QLineEdit* m_lineEdit_configPage_motorPP;
    QPushButton* m_btn_configPage_read_motorPP, *m_btn_configPage_write_motorPP;
    QLineEdit* m_lineEdit_configPage_motorR;
    QPushButton* m_btn_configPage_read_motorR, *m_btn_configPage_write_motorR;
    QLineEdit* m_lineEdit_configPage_motorL;
    QPushButton* m_btn_configPage_read_motorL, *m_btn_configPage_write_motorL;
    QLineEdit* m_lineEdit_configPage_motorCurrentLimit;
    QPushButton* m_btn_configPage_read_motorCurrentLimit, *m_btn_configPage_write_motorCurrentLimit;
    QLineEdit* m_lineEdit_configPage_motorVelLimit;
    QPushButton* m_btn_configPage_read_motorVelLimit, *m_btn_configPage_write_motorVelLimit;

    // 校准
    QLineEdit* m_lineEdit_configPage_calibCurrent;
    QPushButton* m_btn_configPage_read_calibCurrent, *m_btn_configPage_write_calibCurrent;
    QLineEdit* m_lineEdit_configPage_calibVoltage;
    QPushButton* m_btn_configPage_read_calibVoltage, *m_btn_configPage_write_calibVoltage;

    // 控制器
    QComboBox* m_cbox_configPage_ctrlMode;
    QPushButton* m_btn_configPage_read_ctrlMode, *m_btn_configPage_write_ctrlMode;
    QLineEdit* m_lineEdit_configPage_posGain;
    QPushButton* m_btn_configPage_read_posGain, *m_btn_configPage_write_posGain;
    QLineEdit* m_lineEdit_configPage_velGain;
    QPushButton* m_btn_configPage_read_velGain, *m_btn_configPage_write_velGain;
    QLineEdit* m_lineEdit_configPage_velIntegratorGain;
    QPushButton* m_btn_configPage_read_velIntegratorGain, *m_btn_configPage_write_velIntegratorGain;
    QLineEdit* m_lineEdit_configPage_currentCtrlBW;
    QPushButton* m_btn_configPage_read_currentCtrlBW, *m_btn_configPage_write_currentCtrlBW;
    QComboBox* m_cbox_configPage_anticoggingEnable;
    QPushButton* m_btn_configPage_read_anticoggingEnable, *m_btn_configPage_write_anticoggingEnable;
    QComboBox* m_cbox_configPage_syncTargetEnable;
    QPushButton* m_btn_configPage_read_syncTargetEnable, *m_btn_configPage_write_syncTargetEnable;
    QLineEdit* m_lineEdit_configPage_targetVelWindow;
    QPushButton* m_btn_configPage_read_targetVelWindow, *m_btn_configPage_write_targetVelWindow;
    QLineEdit* m_lineEdit_configPage_targetPosWindow;
    QPushButton* m_btn_configPage_read_targetPosWindow, *m_btn_configPage_write_targetPosWindow;
    QLineEdit* m_lineEdit_configPage_torqueRampRate;
    QPushButton* m_btn_configPage_read_torqueRampRate, *m_btn_configPage_write_torqueRampRate;
    QLineEdit* m_lineEdit_configPage_velRampRate;
    QPushButton* m_btn_configPage_read_velRampRate, *m_btn_configPage_write_velRampRate;
    QLineEdit* m_lineEdit_configPage_posFilterBW;
    QPushButton* m_btn_configPage_read_posFilterBW, *m_btn_configPage_write_posFilterBW;
    QLineEdit* m_lineEdit_configPage_profileVel;
    QPushButton* m_btn_configPage_read_profileVel, *m_btn_configPage_write_profileVel;
    QLineEdit* m_lineEdit_configPage_profileAccel;
    QPushButton* m_btn_configPage_read_profileAccel, *m_btn_configPage_write_profileAccel;
    QLineEdit* m_lineEdit_configPage_profileDecel;
    QPushButton* m_btn_configPage_read_profileDecel, *m_btn_configPage_write_profileDecel;

    // 安全保护
    QLineEdit* m_lineEdit_configPage_protectUnderVolt;
    QPushButton* m_btn_configPage_read_protectUnderVolt, *m_btn_configPage_write_protectUnderVolt;
    QLineEdit* m_lineEdit_configPage_protectOverVolt;
    QPushButton* m_btn_configPage_read_protectOverVolt, *m_btn_configPage_write_protectOverVolt;
    QLineEdit* m_lineEdit_configPage_protectOverCurr;
    QPushButton* m_btn_configPage_read_protectOverCurr, *m_btn_configPage_write_protectOverCurr;
    QLineEdit* m_lineEdit_configPage_protectIBusMax;
    QPushButton* m_btn_configPage_read_protectIBusMax, *m_btn_configPage_write_protectIBusMax;

    ///
    /// 实现“配置”界面的读取所有和写入所有
    /// 1. 存放所有的read button
    /// 2. 存放所有的write button
    /// 3. 读取所有操作依次调用所有read button
    /// 4. 写入所有操作依次调用所有write button
    ///
    QVector<QPushButton*>* m_vector_allReadCfgBtn;
    QVector<QPushButton*>* m_vector_allWriteCfgBtn;

    ////////////////////////// 关于界面 //////////////////////////
    QWidget* m_widget_about;

};


class CSerialDataConsumer : public QObject
{
    Q_OBJECT

public:
    explicit CSerialDataConsumer(QObject *parent = nullptr);
    explicit CSerialDataConsumer(lwrb_t* lwrb, QMap<int, QString>* indexToConfig, QMap<QString, int>* configDataType,
                                 QQueue<float>* debugPageScope1DataQueue, QQueue<float>* debugPageScope2DataQueue,
                                 QVector<float>* calibPageScope1DataQueue, QVector<float>* calibPageScope2DataQueue,
                                 QObject *parent = nullptr)
        : m_pSerialLwrb(lwrb),
          m_map_indexToConfig(indexToConfig),
          m_map_configToDataType(configDataType),
          m_queue_debugPageScope1(debugPageScope1DataQueue),
          m_queue_debugPageScope2(debugPageScope2DataQueue),
          m_vector_calibPageScope1(calibPageScope1DataQueue),
          m_vector_calibPageScope2(calibPageScope2DataQueue)
    {
    }

    // 工作函数
    void working();

signals:
    void signal_ShowSystemStatsInfo(QString info);
    void signal_ShowTxErrInfo(QString info);
    void signal_ShowRxErrInfo(QString info);
    void signal_SetMotorEnableLed(bool en);
    void signal_SetTargetReachedLed(bool en);
    void signal_SetCurrentLimitActiveLed(bool en);

    // 发送校准结果的信号
    void signal_SetCalibPageMotorR(float r);
    void signal_SetCalibPageMotorL(float l);
    void signal_SetCalibPageMotorPP(int pp);
    void signal_SetCalibPageEncoderDir(int dir);
    void signal_SetCalibPageEncoderOffset(int offset);


    // 发送读取配置的信号
    void signal_ReadConfig(QString param, int dataType, float val);

    void signal_ShowWarningMessageBox(QString title, QString text);

private:

    // 配置项索引及其对应的配置项
    QMap<int, QString>* m_map_indexToConfig;
    // 配置项对应的数据类型
    QMap<QString, int>* m_map_configToDataType;

    // 串口ring buff
    lwrb_t* m_pSerialLwrb;
    // “调试”界面两个示波器的数据队列
    QQueue<float>* m_queue_debugPageScope1;
    QQueue<float>* m_queue_debugPageScope2;
    // “校准”界面两个示波器的校准数据容器
    QVector<float>* m_vector_calibPageScope1;
    QVector<float>* m_vector_calibPageScope2;
};

Q_DECLARE_METATYPE(eDataType);

#endif // MAINWINDOW_H
